package com.qcby.community.controller;

import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qcby.community.aspect.LogAspect;
import com.qcby.community.entity.User;
import com.qcby.community.form.LoginForm;
import com.qcby.community.service.UserService;
import com.qcby.community.util.JwtUtil;
import com.qcby.community.util.Result;
import com.wf.captcha.SpecCaptcha;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 登录模块业务的控制层接口
 */
@RestController //表明返回的都是json格式数据
public class LoginController {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserService userService;

    @Autowired
    private JwtUtil jwtUtil;

    /**
     * 获取生成验证码的接口
     * @return
     */
    @GetMapping("/captcha")
    public Result getCaptcha(){
        //1.借助于UUID或者其他工具生成一个符合要求的验证码
        //生成验证码的工具类 指定宽和高以及生成的数量4
        //生成的图片 验证码图片对象
        SpecCaptcha specCaptcha = new SpecCaptcha(130,48,4);
        //将工具类生成的验证码图片转义生成大写字符串
        String code = specCaptcha.text().toUpperCase();
        //通过工具类生成一个UUID值 为了存储Redis定位当前验证码使用的
        String uuid = IdUtil.simpleUUID();
        //2.存入到缓存数据库Redis中并设置超时时间120秒
        this.redisTemplate.opsForValue().set(uuid,code,120, TimeUnit.SECONDS);
        //3.验证码返回给前端
        //构建map集合返回给前端 json类型 3指data里的uuid、captcha、code
        Map<String,String> map = new HashMap<>(3);
        //uuid返回给前端的目的是为了登录能够拿着uuid值去Redis查询获取有效的验证码
        map.put("uuid",uuid);
        //4位的验证码
        map.put("code",code);
        //验证码的实际图片 base64 前端可以解析base64的图片进行显示
        map.put("captcha", specCaptcha.toBase64());
        return Result.ok().put("data",map);
    }

    /**
     * 登录的接口
     * 接受前端表单提交的数据
     * captcha、username、password、uuid
     * @return
     */
    @PostMapping("/login")
    public Result login(@RequestBody LoginForm loginForm, HttpSession session){
        //1.查询Redis 验证码是否有效和正确
        String code = (String) this.redisTemplate.opsForValue().get(loginForm.getUuid());
        //判断验证码是否有效
        if(code == null){
            return Result.ok().put("status","fail").put("date","验证码已过期！");
        }
        //判断输入的验证码是否正确 不区分大小写
        if(!code.equalsIgnoreCase(loginForm.getCaptcha())){
            return Result.ok().put("status","fail").put("date","验证码错误！");
        }
        //2.判断用户名是否正确
        //查询数据库验证用户名  使用构造器创建条件
        QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
        queryWrapper.eq("username",loginForm.getUsername());
        //根据用户名查询是否存在该用户 是否正确
        User user = userService.getOne(queryWrapper);
        //判断查询到的用户不等于null 如果等于null就是用户名错误
        if(user == null){
            return Result.error("用户名错误");
        }
        //3.判断密码是否正确  加密  生成token
        String password = SecureUtil.sha256(loginForm.getPassword());
        //判断前端传递的密码（利用SecureUtil工具类采取sha-256方式加密后）是否等于查询回来的user用户的密码
        if(!password.equals(user.getPassword())){
            return Result.error("密码错误");
        }
        //4.验证用户状态是否可用 0为锁定状态
        if(user.getStatus() == 0){
            return Result.error("账号已被锁定，请联系管理员");
        }
        //登录成功
        session.setAttribute("user",user);
        //JWT生成token
        //创建token（令牌）
        String token = this.jwtUtil.createToken(String.valueOf(user.getUserId()));
        //将token存入Redis 目的：每一次访问网站不需要重复登录，验证令牌
        this.redisTemplate.opsForValue().set("communityuser-"+user.getUserId(),token,jwtUtil.getExpire());
        //创建map集合返回
        Map<String,Object> map = new HashMap<>();
        //返回前端需要的令牌token和超时时间
        map.put("token",token);
        map.put("expire",jwtUtil.getExpire());
        //用户日志
        LogAspect.user = user;
        return Result.ok().put("data", map);
    }

    /**
     * 每一次请求都要进行token校验
     * @param request
     * @return
     */
    @GetMapping("/checkToken")
    public Result checkToken(HttpServletRequest request){
        //1.获取请求头中的token
        String token = request.getHeader("token");
        //2.判断token是否有效
        //使用JWT中的checkToken方法和Redis中的token进行加密后的比对
        boolean result = this.jwtUtil.checkToken(token);
        //比对成功
        if(result){
            return Result.ok().put("status","ok");
        }
        //比对失败
        return Result.ok().put("status","error");
    }

    /**
     * 用户退出
     * 从session会话中移除user对象
     * @param session
     * @return
     */
    @PostMapping("/logout")
    public Result logout(HttpSession session){
        //没有使用session移除user 是让session失效
        session.invalidate();
        return Result.ok();
    }


}
